Skip to content

In for-await-of, only call return() for an abrupt completion in user code#51297

Merged
rbuckton merged 1 commit into
mainfrom
fix50525
Oct 28, 2022
Merged

In for-await-of, only call return() for an abrupt completion in user code#51297
rbuckton merged 1 commit into
mainfrom
fix50525

Conversation

@rbuckton

Copy link
Copy Markdown
Contributor

This wraps user code in a for-await-of statement so that we only call iter.return() when user code exits abruptly. This better aligns with ForIn/OfBodyEvaluation which only calls IteratorClose when there is an abrupt completion in the body.

Fixes #50525

@typescript-bot typescript-bot added Author: Team For Milestone Bug PRs that fix a bug with a specific milestone labels Oct 25, 2022
@rbuckton rbuckton changed the title Only call return() for an abrupt completion in user code In for-await-of, only call return() for an abrupt completion in user code Oct 25, 2022

@andrewbranch andrewbranch left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It’s basically impossible to read the emit changes, but the unit test makes it seem like this is right 🤷

@rbuckton

rbuckton commented Oct 28, 2022

Copy link
Copy Markdown
Contributor Author

Basically, the only step of ForIn/OfBodyEvaluation that should trigger IteratorClose is an abrupt completion during the evaluation of the iterator statement body. This just adds a variable that is set to true when entering the loop, temporarily set to false only while evaluating the body statement.

Here's a (hopefully) easier to read version where the temp variables have been renamed to indicate their purpose:

// source
for await (const x of y) {
  ...
}

// output
var _done, _value, _errorFromLoopBody, _returnFunction, _result;
try {
    var _nonUserCode = true; // start non-user code section
    for (var _iter = y[Symbol.asyncIterator](); _result = await _iter.next(), _done = _result.done, !_done;) {
        _value = _result.value;
        _nonUserCode = false; // end non-user code
        // start user code
        try {
            const x = _value;
            ...
        }
        finally {
            // end user code
            _nonUserCode = true; // start non-user code
        }
    }
}
catch (error) {
    _errorFromLoopBody = { error };
}
finally {
    try {
        // only call return if abruptly exiting from _user_ code.
        if (!_nonUserCode && !_done && (_returnFunction = _iter.return)) {
            yield _returnFunction.call(_iter);
        }
    }
    finally {
        if (_errorFromLoopBody) throw _errorFromLoopBody.error;
    }
}
// end non-user code

@rbuckton

Copy link
Copy Markdown
Contributor Author

Looks like this works with main locally.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Author: Team For Milestone Bug PRs that fix a bug with a specific milestone

Projects

None yet

Development

Successfully merging this pull request may close these issues.

for await transform incorrectly calls return when next throws

3 participants